package cn.edu.scau.cmi.ema.service;

import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import cn.edu.scau.cmi.ema.dao.*;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import cn.edu.scau.cmi.ema.domain.base.CmiPagination;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;

import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import javax.persistence.Query;
import java.util.Iterator;
@Service("FillerService")
@Transactional

public class FillerServiceImpl extends CmiEmaService implements FillerService {
	public FillerServiceImpl() {
	}

	@Transactional
	public Filler saveFiller(Filler filler) {
		Set<Fillerfiller> existingFillerfillersForOther = new HashSet<Fillerfiller>();
		Set<Fillerfiller> existingFillerfillersForSelf = new HashSet<Fillerfiller>();
		Set<Fillerhazard> existingFillerhazardsForFiller = new HashSet<Fillerhazard>();
		Set<Foodbatchfiller> existingFoodbatchfillersForFiller = new HashSet<Foodbatchfiller>();

		Set<Fillerfiller> selectedFillerfillersForOther = filler.getFillerfillersForOther();
		Set<Fillerfiller> selectedFillerfillersForSelf = filler.getFillerfillersForSelf();
		Set<Fillerhazard> selectedFillerhazardsForFiller = filler.getFillerhazardsForFiller();
		Set<Foodbatchfiller> selectedFoodbatchfillersForFiller = filler.getFoodbatchfillersForFiller();

		if (filler.getId() == null) {
			Fillertype fillertype = fillertypeDAO.findFillertypeById(filler.getFillertype().getId());

			Filler existingFiller = fillerDAO.findFillerByPhysicalPrimaryKey(filler.getName(), fillertype.getId());

			filler.setFillertype(fillertype);

			if (isEquals(filler, existingFiller)) {
				return existingFiller;
			}

			filler = fillerDAO.store(filler);
			fillerDAO.flush();

			for (Fillerfiller relatedFillerfiller : selectedFillerfillersForOther) {
				relatedFillerfiller.setOther(filler);
				fillerfillerDAO.store(relatedFillerfiller);
			}
			for (Fillerfiller relatedFillerfiller : selectedFillerfillersForSelf) {
				relatedFillerfiller.setSelf(filler);
				fillerfillerDAO.store(relatedFillerfiller);
			}
			for (Fillerhazard relatedFillerhazard : selectedFillerhazardsForFiller) {
				relatedFillerhazard.setFiller(filler);
				fillerhazardDAO.store(relatedFillerhazard);
			}
			for (Foodbatchfiller relatedFoodbatchfiller : selectedFoodbatchfillersForFiller) {
				relatedFoodbatchfiller.setFiller(filler);
				foodbatchfillerDAO.store(relatedFoodbatchfiller);
			}
		} else {
			Filler existingFiller = fillerDAO.findFillerByPrimaryKey(filler.getId());

			existingFillerfillersForOther = existingFiller.getFillerfillersForOther();
			existingFillerfillersForSelf = existingFiller.getFillerfillersForSelf();
			existingFillerhazardsForFiller = existingFiller.getFillerhazardsForFiller();
			existingFoodbatchfillersForFiller = existingFiller.getFoodbatchfillersForFiller();

			Set<Fillerfiller> prepareDeleteFillerfillersForOther = new HashSet<Fillerfiller>();
			Set<Fillerfiller> prepareAddFillerfillersForOther = new HashSet<Fillerfiller>();

			prepareDeleteFillerfillersForOther.addAll(selectedFillerfillersForOther);
			prepareDeleteFillerfillersForOther.addAll(existingFillerfillersForOther);

			prepareAddFillerfillersForOther.addAll(selectedFillerfillersForOther);
			prepareAddFillerfillersForOther.addAll(existingFillerfillersForOther);

			prepareDeleteFillerfillersForOther.removeAll(selectedFillerfillersForOther);
			prepareAddFillerfillersForOther.removeAll(existingFillerfillersForOther);

			for (Fillerfiller relatedFillerfiller : prepareAddFillerfillersForOther) {
				relatedFillerfiller.setOther(filler);
				fillerfillerDAO.store(relatedFillerfiller);
			}

			for (Fillerfiller relatedFillerfiller : prepareDeleteFillerfillersForOther) {
				relatedFillerfiller.setOther(null);
				fillerfillerDAO.store(relatedFillerfiller);
			}
			Set<Fillerfiller> prepareDeleteFillerfillersForSelf = new HashSet<Fillerfiller>();
			Set<Fillerfiller> prepareAddFillerfillersForSelf = new HashSet<Fillerfiller>();

			prepareDeleteFillerfillersForSelf.addAll(selectedFillerfillersForSelf);
			prepareDeleteFillerfillersForSelf.addAll(existingFillerfillersForSelf);

			prepareAddFillerfillersForSelf.addAll(selectedFillerfillersForSelf);
			prepareAddFillerfillersForSelf.addAll(existingFillerfillersForSelf);

			prepareDeleteFillerfillersForSelf.removeAll(selectedFillerfillersForSelf);
			prepareAddFillerfillersForSelf.removeAll(existingFillerfillersForSelf);

			for (Fillerfiller relatedFillerfiller : prepareAddFillerfillersForSelf) {
				relatedFillerfiller.setSelf(filler);
				fillerfillerDAO.store(relatedFillerfiller);
			}

			for (Fillerfiller relatedFillerfiller : prepareDeleteFillerfillersForSelf) {
				relatedFillerfiller.setSelf(null);
				fillerfillerDAO.store(relatedFillerfiller);
			}
			Set<Fillerhazard> prepareDeleteFillerhazardsForFiller = new HashSet<Fillerhazard>();
			Set<Fillerhazard> prepareAddFillerhazardsForFiller = new HashSet<Fillerhazard>();

			prepareDeleteFillerhazardsForFiller.addAll(selectedFillerhazardsForFiller);
			prepareDeleteFillerhazardsForFiller.addAll(existingFillerhazardsForFiller);

			prepareAddFillerhazardsForFiller.addAll(selectedFillerhazardsForFiller);
			prepareAddFillerhazardsForFiller.addAll(existingFillerhazardsForFiller);

			prepareDeleteFillerhazardsForFiller.removeAll(selectedFillerhazardsForFiller);
			prepareAddFillerhazardsForFiller.removeAll(existingFillerhazardsForFiller);

			for (Fillerhazard relatedFillerhazard : prepareAddFillerhazardsForFiller) {
				relatedFillerhazard.setFiller(filler);
				fillerhazardDAO.store(relatedFillerhazard);
			}

			for (Fillerhazard relatedFillerhazard : prepareDeleteFillerhazardsForFiller) {
				relatedFillerhazard.setFiller(null);
				fillerhazardDAO.store(relatedFillerhazard);
			}
			Set<Foodbatchfiller> prepareDeleteFoodbatchfillersForFiller = new HashSet<Foodbatchfiller>();
			Set<Foodbatchfiller> prepareAddFoodbatchfillersForFiller = new HashSet<Foodbatchfiller>();

			prepareDeleteFoodbatchfillersForFiller.addAll(selectedFoodbatchfillersForFiller);
			prepareDeleteFoodbatchfillersForFiller.addAll(existingFoodbatchfillersForFiller);

			prepareAddFoodbatchfillersForFiller.addAll(selectedFoodbatchfillersForFiller);
			prepareAddFoodbatchfillersForFiller.addAll(existingFoodbatchfillersForFiller);

			prepareDeleteFoodbatchfillersForFiller.removeAll(selectedFoodbatchfillersForFiller);
			prepareAddFoodbatchfillersForFiller.removeAll(existingFoodbatchfillersForFiller);

			for (Foodbatchfiller relatedFoodbatchfiller : prepareAddFoodbatchfillersForFiller) {
				relatedFoodbatchfiller.setFiller(filler);
				foodbatchfillerDAO.store(relatedFoodbatchfiller);
			}

			for (Foodbatchfiller relatedFoodbatchfiller : prepareDeleteFoodbatchfillersForFiller) {
				relatedFoodbatchfiller.setFiller(null);
				foodbatchfillerDAO.store(relatedFoodbatchfiller);
			}
			filler = fillerDAO.store(filler);
			fillerDAO.flush();
		}

		fillerfillerDAO.flush();
		fillerfillerDAO.flush();
		fillerhazardDAO.flush();
		foodbatchfillerDAO.flush();
		return filler;
	}

	public void deleteFiller(Filler filler) {
		fillerDAO.remove(filler);
		fillerDAO.flush();
	}

	@Transactional
	public void deleteFillers(Set<Integer> deleteFillersId) {
		Query query = fillerDAO.createNamedQuery("deleteFillersById", -1, -1);
		query.setParameter(1, deleteFillersId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Filler> loadFillers() {
		return fillerDAO.findAllFillers();

	}

	@Transactional
	public Set<Filler> loadReferenceFillers(Set<Filler> fillers) {
		Iterator<Filler> fillersIterator = fillers.iterator();
		Set<Filler> referencedFillers = new HashSet<>();
		while (fillersIterator.hasNext()) {
			Filler currentFiller = fillersIterator.next();

			if (!currentFiller.getFillerfillersForOther().isEmpty()) {
				referencedFillers.add(currentFiller);
				continue;
			}

			if (!currentFiller.getFillerfillersForSelf().isEmpty()) {
				referencedFillers.add(currentFiller);
				continue;
			}

			if (!currentFiller.getFillerhazardsForFiller().isEmpty()) {
				referencedFillers.add(currentFiller);
				continue;
			}

			if (!currentFiller.getFoodbatchfillersForFiller().isEmpty()) {
				referencedFillers.add(currentFiller);
				continue;
			}
		}
		return referencedFillers;
	}

	public Set<Filler> loadFillersByIdSet(Set<Integer> fillerIdSet) {

		Iterator<Integer> fillerIdIterator = fillerIdSet.iterator();
		Set<Filler> fillerSet = new HashSet<>();
		while (fillerIdIterator.hasNext()) {
			Integer id = fillerIdIterator.next();
			Filler currentFiller = fillerDAO.findFillerById(id);
			fillerSet.add(currentFiller);
		}
		return fillerSet;
	}

	@Transactional
	public List<Filler> findAllFillers(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Filler>(fillerDAO.findAllFillers(startResult, maxRows));
	}

	@Transactional
	public Filler findFillerByPrimaryKey(Integer id) {
		return fillerDAO.findFillerByPrimaryKey(id);
	}

	@Transactional
	public Integer countFillers() {
		return ((Long) fillerDAO.createQuerySingleResult("select count(o) from Filler o").getSingleResult()).intValue();
	}

	@Transactional
	public Integer countFillers(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Filler.class);
		return ((Long) fillerDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 * 
	 */
	@Transactional
	public Integer countFillersByName(String queryName) {
		Query query = fillerDAO.createNamedQuery("countFillersByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countFillersByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Filler.class);
		Session session = entityManager.unwrap(Session.class);
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadFillerObjectReferencedSet(Filler filler) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();
		if (filler.getFillertype() != null) {

			Set<Integer> fillertypeIds = new HashSet<Integer>();
			fillertypeIds.add(filler.getFillertype().getId());
			objectReferencedSetMap.put("fillertypeIds", fillertypeIds);
		}

		Set<Integer> fillerfillersIds = new HashSet<Integer>();
		for (Fillerfiller fillerfiller : filler.getFillerfillersForOther()) {
			fillerfillersIds.add(fillerfiller.getId());
		}
		objectReferencedSetMap.put("fillerfillersIds", fillerfillersIds);
		Set<Integer> fillerhazardsIds = new HashSet<Integer>();
		for (Fillerhazard obj : filler.getFillerhazardsForFiller()) {

			fillerhazardsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("fillerhazardsIds", fillerhazardsIds);
		Set<Integer> foodbatchfillersIds = new HashSet<Integer>();
		for (Foodbatchfiller obj : filler.getFoodbatchfillersForFiller()) {

			foodbatchfillersIds.add(obj.getId());
		}
		objectReferencedSetMap.put("foodbatchfillersIds", foodbatchfillersIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadFillerClassReferencedSet(Filler filler) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("fillertypes", fillertypeDAO.findAllFillertypes());
		classReferencedSetMap.put("fillerfillers", fillerfillerDAO.findAllFillerfillers());
		classReferencedSetMap.put("fillerhazards", fillerhazardDAO.findAllFillerhazards());
		classReferencedSetMap.put("foodbatchfillers", foodbatchfillerDAO.findAllFoodbatchfillers());

		return classReferencedSetMap;
	}

	public boolean isEquals(Filler filler, Object obj) {
		if (obj == filler)
			return true;
		if (!(obj instanceof Filler))
			return false;
		Filler comparedFiller = (Filler) obj;
		if (!CheckPropertyEqual(filler.getName(), comparedFiller.getName())) {
			return false;
		}
		if (!CheckPropertyEqual(filler.getFillertype().getId(), comparedFiller.getFillertype().getId())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

